package com.winning.pmph.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.winning.pmph.entity.*;
import com.winning.pmph.mapper.FileLibMapper;
import com.winning.pmph.utils.ApplicationContextUtil;
import com.winning.pmph.utils.Const;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author admin
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class FileLibService extends ServiceImpl<FileLibMapper, FileLib> {

    @Resource
    private UncommonWordService uncommonWordService;
    @Resource
    private FileLibRefService fileLibRefService;

    /**
     * 知识发布时,保存静态资源
     * 可能存在图片的字段类型  0 文本框  1 富文本  4 图片 6 文本域  7 多值  11 视频  12 文件
     *
     * @param fieldList
     */
    public void insertKnowledgeFileLib(Knowledge knowledge, List<ReleasedKnowledgeField> fieldList) {
        // 先删除两张静态资源表的数据,避免数据重复,先删除的目的是避免一张一张的去对比
        List<FileLibRef> libRefList = fileLibRefService.lambdaQuery().eq(FileLibRef::getKnowledgeId, knowledge.getId()).list();
        // 删除静态资源表
        removeByIds(libRefList.stream().map(FileLibRef::getFileLibId).collect(Collectors.toList()));
        // 删除关联关系表
        fileLibRefService.removeByIds(libRefList.stream().map(FileLibRef::getId).collect(Collectors.toList()));
        List<FileLib> fileLibList = new ArrayList<>();
        List<String> uncommonIdList = uncommonWordService.list().stream().map(UncommonWord::getId).collect(Collectors.toList());
        fieldList.forEach(fieldItem -> {
            // 如果为空或者不包含图片,视频,文件,直接跳过
            if (Const.KNOWLEDGE_FIELD_VALUE_DEFAULT.contains(fieldItem.getProperty()) || !fieldItem.getProperty().contains("filename=")) {
                return;
            }
            // 富文本中可能含有图片,视频,图片有图题,图注
            fieldItem.setKnowledgeLibId(knowledge.getKnowledgeLibId());
            fieldItem.setKnowledgeName(knowledge.getName());
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_RICHEDITOR)) {
                // 解析图片
                parseRichEditorImg(fieldItem, uncommonIdList, fileLibList);
                // 解析视频
                parseRichEditorVideo(fieldItem, fileLibList);
                return;
            }
            // 行内图
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_STRINGTEXT) ||
                    fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_TEXTAREA) ||
                    fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_MULTI_VALUE_INPUT)) {
                parseInlineImg(fieldItem, uncommonIdList, fileLibList);
                return;
            }
            // 图片类型
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_IMG)) {
                parseTypeImage(fieldItem, fileLibList);
                return;
            }
            // 视频类型
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_VIDEO)) {
                parseTypeVideo(fieldItem, fileLibList);
                return;
            }
            // 文件类型
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_FILE)) {
                parseTypeFile(fieldItem, fileLibList);
                return;
            }
            // 音频类型
            if (fieldItem.getKind().equals(KnowledgeLibField.FIELD_KIND_AUDIO)) {
                parseTypeAudio(fieldItem, fileLibList);
                return;
            }
        });
        saveBatch(fileLibList);
        fileLibRefService.saveBatch(fileLibList.stream().map(FileLib::getFileLibRef).collect(Collectors.toList()));
    }

    private void parseRichEditorImg(KnowledgeField field, List<String> uncommonIdList, List<FileLib> fileLibList) {
        // 判断组图,组图只能以组为单位引用
        Element body = Jsoup.parse(field.getProperty()).body();
        Elements ztImgEles = body.getElementsByAttributeValue("class", "zt");
        for (Element ztImgEle : ztImgEles) {
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            fileLib.setPath(new JSONArray(ztImgEle.children().stream().map(item -> item.attr("src")).collect(Collectors.toList())));
            fileLib.setType(Const.FILE_TYPE_IMAGE);
            fileLib.setClassType(StringUtils.isBlank(ztImgEle.child(0).attr("class")) ?
                    null : "rw_img " + ztImgEle.child(0).attr("class").split(" ")[1]);
            fileLib.setAdditional(ztImgEle.child(0).attr("data-additional"));
            fileLib.setClassification(ztImgEle.child(0).attr("data-classification"));
            fileLib.setCategory(Const.FILE_LIB_TYPE_GROUP);
            fileLib.setMd5(ztImgEle.children().stream()
                    .map(item -> {
                        try {
                            return DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                                    StringUtils.substringAfterLast(item.attr("src"), "filename=")));
                        } catch (IOException e) {
                            throw new RuntimeException("获取图片的md5失败" + e);
                        }
                    }).collect(Collectors.joining(",")));
            parseImgInfo(ztImgEle.child(0), fileLib);
            // 移除组图
            ztImgEle.remove();
        }
        Elements imgEles = body.getElementsByTag("img");
        for (Element imgEle : imgEles) {
            // 判断图片是否属于生僻字,如果属于生僻字,跳过
            if (uncommonIdList.contains(imgEle.attr("data-bz-id"))) {
                continue;
            }
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            String imgSrc = ApplicationContextUtil.BASE_DOWN_FORDER + StringUtils.substringAfterLast(imgEle.attr("src"), "filename=");
            // 取得图片的md5
            try {
                String md5DigestAsHex = DigestUtils.md5DigestAsHex(new FileInputStream(imgSrc));
                fileLib.setMd5(md5DigestAsHex);
                fileLib.setPath(new JSONArray(Lists.newArrayList(imgEle.attr("src"))));
                fileLib.setType(Const.FILE_TYPE_IMAGE);
                fileLib.setClassType(StringUtils.isBlank(imgEle.attr("class")) ?
                        null : "rw_img " + imgEle.attr("class").split(" ")[1]);
                // 取得图片的高
                int height = ImageIO.read(new File(imgSrc)).getHeight();
                // 行内图
                if (height < 30 || StringUtils.equals(imgEle.attr("class"), "rw_img hnt")) {
                    return;
                }
            } catch (IOException e) {
                throw new RuntimeException("获取图片的md5失败" + e);
            }
            // 取得图片的补充类型和图片分类
            fileLib.setAdditional(imgEle.attr("data-additional"));
            fileLib.setClassification(imgEle.attr("data-classification"));
            parseImgInfo(imgEle, fileLib);
        }
    }

    /**
     * 取得图片的图题图注
     *
     * @param img
     * @param fileLib
     */
    private void parseImgInfo(Element img, FileLib fileLib) {
        Element parentNode = img.parent();
        // 取得上一个兄弟节点
        Element preSibling = parentNode.previousElementSibling();
        // 表题, 可能没有表题
        if (Objects.nonNull(preSibling) && StringUtils.equals(preSibling.className(), "t_title")) {
            fileLib.setTitle(preSibling.html());
        }
        // 取得下一个兄弟节点
        Element nextSibling = parentNode.nextElementSibling();
        if (Objects.isNull(nextSibling)) {
            return;
        }
        // 表注/图注(没有图题,只有图注)
        if (StringUtils.isNotBlank(nextSibling.className()) && nextSibling.className().contains("note")) {
            fileLib.setNote(nextSibling.html());
            return;
        }
        // 图题
        if (StringUtils.equals(nextSibling.className(), "p_title")) {
            fileLib.setTitle(nextSibling.html());
            nextSibling = nextSibling.nextElementSibling();
        }
        // 图注
        if (Objects.nonNull(nextSibling) && StringUtils.equals(nextSibling.className(), "p_note")) {
            fileLib.setNote(nextSibling.html());
        }
    }

    private void parseRichEditorVideo(KnowledgeField field, List<FileLib> fileLibList) {
        // 判断组视频,组视频可以被单个引用
        Element body = Jsoup.parse(field.getProperty()).body();
        Elements ztVideoEles = body.getElementsByAttributeValue("class", "zsp");
        for (Element ztVideoEle : ztVideoEles) {
            for (Element child : ztVideoEle.children()) {
                FileLib fileLib = new FileLib();
                fileLibList.add(fileLib);
                fileLib.setId(IdWorker.getIdStr());
                fileLib.setSource(field.getKnowledgeName());
                fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                        field.getKnowledgeId(), field.getId()));
                fileLib.setPath(new JSONArray(Lists.newArrayList(child.attr("src"))));
                fileLib.setType(Const.FILE_TYPE_VIDEO);
                fileLib.setClassType(child.attr("class"));
                fileLib.setCategory(Const.FILE_LIB_TYPE_GROUP);
                try {
                    fileLib.setMd5(DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                            StringUtils.substringAfterLast(child.attr("src"), "filename="))));
                } catch (IOException e) {
                    throw new RuntimeException("获取视频的md5失败");
                }
                parseVideoInfo(child, fileLib);
            }
            // 移除组视频
            ztVideoEle.remove();
        }
        Elements videoEles = body.getElementsByTag("video");
        for (Element videoEle : videoEles) {
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            String imgSrc = ApplicationContextUtil.BASE_DOWN_FORDER + StringUtils.substringAfterLast(videoEle.attr("src"), "filename=");
            // 取得视频的md5
            try {
                String md5DigestAsHex = DigestUtils.md5DigestAsHex(new FileInputStream(imgSrc));
                fileLib.setMd5(md5DigestAsHex);
                fileLib.setPath(new JSONArray(Lists.newArrayList(videoEle.attr("src"))));
                fileLib.setType(Const.FILE_TYPE_VIDEO);
                fileLib.setClassType(videoEle.attr("class"));
                parseVideoInfo(videoEle, fileLib);
            } catch (IOException e) {
                throw new RuntimeException("获取视频的md5失败" + e);
            }
        }
    }

    /**
     * 取得视频的的标题
     *
     * @param video
     * @param fileLib
     */
    private void parseVideoInfo(Element video, FileLib fileLib) {
        Element parentNode = video.parent();
        // 取得上一个兄弟节点
        Element preSibling = parentNode.previousElementSibling();
        // 表题, 可能没有表题
        if (Objects.nonNull(preSibling) && StringUtils.equals(preSibling.className(), "v_title")) {
            fileLib.setTitle(preSibling.html());
        }
    }

    private void parseInlineImg(KnowledgeField field, List<String> uncommonIdList, List<FileLib> fileLibList) {
        Element body = Jsoup.parse(field.getProperty()).body();
        Elements imgEles = body.getElementsByTag("img");
        for (Element imgEle : imgEles) {
            // 判断图片是否属于生僻字,如果属于生僻字,跳过
            if (uncommonIdList.contains(StringUtils.defaultIfBlank(imgEle.attr("data-bz-id"),
                    "").replace("\\\"", ""))) {
                continue;
            }
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            String imgSrc = ApplicationContextUtil.BASE_DOWN_FORDER + StringUtils.substringAfterLast(imgEle.attr("src"), "filename=");
            // 取得图片的md5
            try {
                String md5DigestAsHex = DigestUtils.md5DigestAsHex(new FileInputStream(imgSrc));
                fileLib.setMd5(md5DigestAsHex);
                fileLib.setPath(new JSONArray(Lists.newArrayList(imgEle.attr("src"))));
                fileLib.setType(Const.FILE_TYPE_IMAGE);
                fileLib.setClassType(imgEle.attr("class"));
            } catch (IOException e) {
                throw new RuntimeException("获取图片的md5失败" + e);
            }
        }
    }

    private void parseTypeImage(KnowledgeField field, List<FileLib> fileLibList) {
        JSONObject jsonObject = JSON.parseObject(field.getProperty());
        FileLib fileLib = new FileLib();
        fileLibList.add(fileLib);
        fileLib.setId(IdWorker.getIdStr());
        fileLib.setSource(field.getKnowledgeName());
        fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                field.getKnowledgeId(), field.getId()));
        fileLib.setPath(jsonObject.getJSONArray("paths"));
        fileLib.setType(Const.FILE_TYPE_IMAGE);
        fileLib.setClassType(jsonObject.getString("type"));
        fileLib.setAdditional(jsonObject.getString("additional"));
        fileLib.setClassification(jsonObject.getString("classification"));
        fileLib.setTitle(jsonObject.getString("title"));
        fileLib.setNote(jsonObject.getString("comment"));
        fileLib.setMd5(jsonObject.getJSONArray("paths").stream()
                .map(item -> {
                    try {
                        return DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                                StringUtils.substringAfterLast(item.toString(), "filename=")));
                    } catch (IOException e) {
                        throw new RuntimeException("获取图片的md5失败" + e);
                    }
                }).collect(Collectors.joining(",")));
    }

    private void parseTypeVideo(KnowledgeField field, List<FileLib> fileLibList) {
        JSONObject jsonObject = JSON.parseObject(field.getProperty());
        JSONArray paths = jsonObject.getJSONArray("paths");
        for (Object videoPath : paths) {
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            fileLib.setPath(new JSONArray(Lists.newArrayList(videoPath)));
            fileLib.setType(Const.FILE_TYPE_VIDEO);
            fileLib.setTitle(jsonObject.getString("title"));
            fileLib.setClassification(jsonObject.getString("type"));
            try {
                fileLib.setMd5(DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                        StringUtils.substringAfterLast(videoPath.toString(), "filename="))));
            } catch (IOException e) {
                throw new RuntimeException("获取视频的md5失败" + e);
            }
        }
    }

    private void parseTypeFile(KnowledgeField field, List<FileLib> fileLibList) {
        JSONArray jsonArray = JSONArray.parseArray(field.getProperty());
        for (Object fileObject : jsonArray) {
            JSONObject jsonObject = (JSONObject) fileObject;
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            fileLib.setPath(new JSONArray(Lists.newArrayList(jsonObject.getString("path"))));
            fileLib.setType(Const.FILE_TYPE_FILE);
            fileLib.setTitle(jsonObject.getString("name"));
            try {
                fileLib.setMd5(DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                        StringUtils.substringAfterLast(jsonObject.getString("path"), "filename="))));
            } catch (IOException e) {
                throw new RuntimeException("获取文件的md5失败" + e);
            }
        }
    }

    private void parseTypeAudio(KnowledgeField field, List<FileLib> fileLibList) {
        JSONObject jsonObject = JSON.parseObject(field.getProperty());
        JSONArray paths = jsonObject.getJSONArray("paths");
        for (Object audioPath : paths) {
            FileLib fileLib = new FileLib();
            fileLibList.add(fileLib);
            fileLib.setId(IdWorker.getIdStr());
            fileLib.setSource(field.getKnowledgeName());
            fileLib.setFileLibRef(new FileLibRef(IdWorker.getIdStr(), fileLib.getId(), field.getKnowledgeLibId(),
                    field.getKnowledgeId(), field.getId()));
            fileLib.setPath(new JSONArray(Lists.newArrayList(audioPath)));
            fileLib.setType(Const.FILE_TYPE_AUDIO);
            fileLib.setTitle(jsonObject.getString("title"));
            fileLib.setClassification(jsonObject.getString("type"));
            try {
                fileLib.setMd5(DigestUtils.md5DigestAsHex(new FileInputStream(ApplicationContextUtil.BASE_DOWN_FORDER +
                        StringUtils.substringAfterLast(audioPath.toString(), "filename="))));
            } catch (IOException e) {
                throw new RuntimeException("获取音频的md5失败" + e);
            }
        }
    }
}


